home *** CD-ROM | disk | FTP | other *** search
/ Aminet 21 / Aminet 21 (1997)(GTI - Schatztruhe)[!][Oct 1997].iso / Aminet / gfx / show / gs_src_gs.lha / gs5.03 / imainarg.c < prev    next >
C/C++ Source or Header  |  1997-05-13  |  19KB  |  705 lines

  1. /* Copyright (C) 1996, 1997 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of Aladdin Ghostscript.
  4.   
  5.   Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  6.   or distributor accepts any responsibility for the consequences of using it,
  7.   or for whether it serves any particular purpose or works at all, unless he
  8.   or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  9.   License (the "License") for full details.
  10.   
  11.   Every copy of Aladdin Ghostscript must include a copy of the License,
  12.   normally in a plain ASCII text file named PUBLIC.  The License grants you
  13.   the right to copy, modify and redistribute Aladdin Ghostscript, but only
  14.   under certain conditions described in the License.  Among other things, the
  15.   License requires that the copyright notice and this notice be preserved on
  16.   all copies.
  17. */
  18.  
  19. /* imainarg.c */
  20. /* Command line parsing and dispatching */
  21. /* Define PROGRAM_NAME before we include std.h */
  22. #define PROGRAM_NAME gs_product
  23. #include "ctype_.h"
  24. #include "memory_.h"
  25. #include "string_.h"
  26. #include "ghost.h"
  27. #include "gp.h"
  28. #include "gsargs.h"
  29. #include "gscdefs.h"
  30. #include "gsmdebug.h"
  31. #include "gxdevice.h"
  32. #include "gxdevmem.h"
  33. #include "gsdevice.h"
  34. #include "stream.h"
  35. #include "errors.h"
  36. #include "estack.h"
  37. #include "ialloc.h"
  38. #include "strimpl.h"        /* for sfilter.h */
  39. #include "sfilter.h"        /* for iscan.h */
  40. #include "ostack.h"        /* must precede iscan.h */
  41. #include "iscan.h"
  42. #include "imain.h"
  43. #include "imainarg.h"
  44. #include "iminst.h"
  45. #include "iname.h"
  46. #include "store.h"
  47. #include "files.h"                /* requires stream.h */
  48. #include "interp.h"
  49. #include "iutil.h"
  50. #include "ivmspace.h"
  51.  
  52. /* Import operator procedures */
  53. extern int zflush (P1(os_ptr));
  54. extern int zflushpage (P1(os_ptr));
  55.  
  56. #ifndef GS_LIB
  57. #  define GS_LIB "GS_LIB"
  58. #endif
  59.  
  60. #ifndef GS_OPTIONS
  61. #  define GS_OPTIONS "GS_OPTIONS"
  62. #endif
  63.  
  64. #ifndef GS_MAX_LIB_DIRS
  65. #  define GS_MAX_LIB_DIRS 25
  66. #endif
  67.  
  68. #ifndef GS_BUG_MAILBOX
  69. #  define GS_BUG_MAILBOX "ghost@aladdin.com"
  70. #endif
  71.  
  72. /* Library routines not declared in a standard header */
  73. extern char *getenv(P1(const char *));
  74. /* Note: sscanf incorrectly defines its first argument as char * */
  75. /* rather than const char *.  This accounts for the ugly casts below. */
  76.  
  77. /* Redefine puts to use fprintf, so it will work even without stdio. */
  78. #undef puts
  79. private void near
  80. fpputs(const char *str)
  81. {    fprintf(stdout, "%s\n", str);
  82. }
  83. #define puts(str) fpputs(str)
  84.  
  85. /* Other imported data */
  86. extern const char *gs_doc_directory;
  87. extern const char *gs_lib_default_path;
  88. extern ref gs_emulator_name_array[];
  89. extern long gs_malloc_limit;
  90.  
  91. /* Forward references */
  92. private int swproc(P3(gs_main_instance *, const char *, arg_list *));
  93. private void argproc(P2(gs_main_instance *, const char *));
  94. private int esc_strlen(P1(const char *));
  95. private void esc_strcat(P2(char *, const char *));
  96. private void runarg(P6(gs_main_instance *, const char *, const char *, const char *, bool, bool));
  97. private void run_string(P3(gs_main_instance *, const char *, bool));
  98. private void run_finish(P3(int, int, ref *));
  99.  
  100. /* Forward references for help printout */
  101. private void print_help(P1(gs_main_instance *));
  102. private void print_revision(P0());
  103. private void print_version(P0());
  104. private void print_usage(P0());
  105. private void print_devices(P0());
  106. private void print_emulators(P0());
  107. private void print_paths(P1(gs_main_instance *));
  108. private void print_help_trailer(P0());
  109.  
  110. /* ------ Main program ------ */
  111.  
  112. /* Process the command line with a given instance. */
  113. private FILE *
  114. gs_main_arg_fopen(const char *fname, void *vminst)
  115. {    gs_main_set_lib_paths((gs_main_instance *)vminst);
  116.     return lib_fopen(fname);
  117. }
  118. #define arg_heap_copy(str) arg_copy(str, &gs_memory_default)
  119. int
  120. gs_main_init_with_args(gs_main_instance *minst, int argc, char *argv[])
  121. {    const char *arg;
  122.     arg_list args;
  123.     FILE *stdfiles[3];
  124.  
  125.     gs_get_real_stdio(stdfiles);
  126.     arg_init(&args, (const char **)argv, argc,
  127.          gs_main_arg_fopen, (void *)minst);
  128.     gs_main_init0(minst, stdfiles[0], stdfiles[1], stdfiles[2],
  129.               GS_MAX_LIB_DIRS);
  130.        {    char *lib = getenv(GS_LIB);
  131.         if ( lib != 0 ) 
  132.            {    int len = strlen(lib);
  133.             char *path = gs_malloc(len + 1, 1, "GS_LIB");
  134.             strcpy(path, lib);
  135.             minst->lib_path.env = path;
  136.            }
  137.        }
  138.     minst->lib_path.final = gs_lib_default_path;
  139.     gs_main_set_lib_paths(minst);
  140.     /* Prescan the command line for --help and --version. */
  141.     { int i;
  142.       bool helping = false;
  143.       for ( i = 1; i < argc; ++i )
  144.         if ( !strcmp(argv[i], "--" ) )
  145.           { /* A PostScript program will be interpreting all the */
  146.         /* remaining switches, so stop scanning. */
  147.         helping = false;
  148.         break;
  149.           }
  150.         else if ( !strcmp(argv[i], "--help") )
  151.           { print_help(minst);
  152.         helping = true;
  153.           }
  154.         else if ( !strcmp(argv[i], "--version") )
  155.           { print_version();
  156.         puts("");        /* \n */
  157.         helping = true;
  158.           }
  159.       if ( helping )
  160.         gs_exit(gs_exit_INFO);
  161.     }
  162.     /* Execute files named in the command line, */
  163.     /* processing options along the way. */
  164.     /* Wait until the first file name (or the end */
  165.     /* of the line) to finish initialization. */
  166.     minst->run_start = true;
  167.     {    const char *opts = getenv(GS_OPTIONS);
  168.         if ( opts != 0 )
  169.             arg_push_string(&args, opts);
  170.     }
  171.     while ( (arg = arg_next(&args)) != 0 )
  172.        {    switch ( *arg )
  173.         {
  174.         case '-':
  175.             if ( swproc(minst, arg, &args) < 0 )
  176.               fprintf(stdout,
  177.                   "Unknown switch %s - ignoring\n", arg);
  178.             break;
  179.         default:
  180.             argproc(minst, arg);
  181.         }
  182.        }
  183.  
  184.     gs_main_init2(minst);
  185.  
  186.     return 0;
  187. }
  188.  
  189. /* Run the 'start' procedure (after processing the command line). */
  190. /* Note that this procedure exits rather than returning. */
  191. void
  192. gs_main_run_start(gs_main_instance *minst)
  193. {    run_string(minst, "systemdict /start get exec", true);
  194. }
  195.  
  196. /* Process switches */
  197. private int
  198. swproc(gs_main_instance *minst, const char *arg, arg_list *pal)
  199. {    char sw = arg[1];
  200.     ref vtrue;
  201.  
  202.     make_true(&vtrue);
  203.     arg += 2;        /* skip - and letter */
  204.     switch ( sw )
  205.        {
  206.     default:
  207.         return -1;
  208.     case 0:                /* read stdin as a file */
  209.         minst->run_start = false;    /* don't run 'start' */
  210.         /* Set NOPAUSE so showpage won't try to read from stdin. */
  211.         swproc(minst, "-dNOPAUSE", pal);
  212.         gs_main_init2(minst);        /* Finish initialization */
  213.         /* We delete this only to make Ghostview work properly. */
  214.         /**************** This is WRONG. ****************/
  215.         /*gs_stdin_is_interactive = false;*/
  216.         run_string(minst, ".runstdin", true);
  217.         break;
  218.     case '-':            /* run with command line args */
  219.     case '+':
  220.         pal->expand_ats = false;
  221.     case '@':            /* ditto with @-expansion */
  222.        {    const char *psarg = arg_next(pal);
  223.         if ( psarg == 0 )
  224.         {    fprintf(stdout, "Usage: gs ... -%c file.ps arg1 ... argn\n", sw);
  225.             arg_finit(pal);
  226.             gs_exit(1);
  227.         }
  228.         psarg = arg_heap_copy(psarg);
  229.         gs_main_init2(minst);
  230.         run_string(minst, "userdict/ARGUMENTS[", false);
  231.         while ( (arg = arg_next(pal)) != 0 )
  232.           runarg(minst, "", arg_heap_copy(arg), "", true, false);
  233.         runarg(minst, "]put", psarg, ".runfile", true, true);
  234.         gs_exit(0);
  235.        }
  236.     case 'A':            /* trace allocator */
  237.         switch ( *arg )
  238.         {
  239.         case 0: gs_alloc_debug = 1; break;
  240.         case '-': gs_alloc_debug = 0; break;
  241.         default: puts("-A may only be followed by -"); gs_exit(1);
  242.         }
  243.         break;
  244.     case 'c':            /* code follows */
  245.       {    bool ats = pal->expand_ats;
  246.         gs_main_init2(minst);
  247.         pal->expand_ats = false;
  248.         while ( (arg = arg_next(pal)) != 0 )
  249.           {    char *sarg;
  250.             if ( arg[0] == '@' ||
  251.                  (arg[0] == '-' && !isdigit(arg[1]))
  252.                )
  253.               break;
  254.             sarg = arg_heap_copy(arg);
  255.             runarg(minst, "", sarg, ".runstring", false, false);
  256.           }
  257.         if ( arg != 0 )
  258.           arg_push_string(pal, arg_heap_copy(arg));
  259.         pal->expand_ats = ats;
  260.         break;
  261.       }
  262.     case 'E':            /* log errors */
  263.         switch ( *arg )
  264.         {
  265.         case 0: gs_log_errors = 1; break;
  266.         case '-': gs_log_errors = 0; break;
  267.         default: puts("-E may only be followed by -"); gs_exit(1);
  268.         }
  269.         break;
  270.     case 'f':            /* run file of arbitrary name */
  271.         if ( *arg != 0 )
  272.           argproc(minst, arg);
  273.         break;
  274.     case 'g':            /* define device geometry */
  275.        {    long width, height;
  276.         ref value;
  277.         gs_main_init1(minst);
  278.         if ( sscanf((const char *)arg, "%ldx%ld", &width, &height) != 2 )
  279.            {    puts("-g must be followed by <width>x<height>");
  280.             gs_exit(1);
  281.            }
  282.         make_int(&value, width);
  283.         initial_enter_name("DEVICEWIDTH", &value);
  284.         make_int(&value, height);
  285.         initial_enter_name("DEVICEHEIGHT", &value);
  286.         initial_enter_name("FIXEDMEDIA", &vtrue);
  287.         break;
  288.        }
  289.     case 'h':            /* print help */
  290.     case '?':            /* ditto */
  291.         print_help(minst);
  292.         gs_exit(gs_exit_INFO);
  293.     case 'I':            /* specify search path */
  294.         gs_main_add_lib_path(minst, arg_heap_copy(arg));
  295.         break;
  296.     case 'K':            /* set malloc limit */
  297.       {    long msize = 0;
  298.         sscanf((const char *)arg, "%ld", &msize);
  299.         if ( msize <= 0 || msize > max_long >> 10 )
  300.           { fprintf(stdout, "-K<numK> must have 1 <= numK <= %ld\n",
  301.                 max_long >> 10);
  302.             gs_exit(1);
  303.           }
  304.         gs_malloc_limit = msize << 10;
  305.       }
  306.         break;
  307.     case 'M':            /* set memory allocation increment */
  308.        {    unsigned msize = 0;
  309.         sscanf((const char *)arg, "%u", &msize);
  310. #if arch_ints_are_short
  311.         if ( msize <= 0 || msize >= 64 )
  312.            {    puts("-M must be between 1 and 63");
  313.             gs_exit(1);
  314.            }
  315. #endif
  316.         minst->memory_chunk_size = msize << 10;
  317.        }
  318.         break;
  319.     case 'N':            /* set size of name table */
  320.        {    unsigned nsize = 0;
  321.         sscanf((const char *)arg, "%d", &nsize);
  322. #if arch_ints_are_short
  323.         if ( nsize < 2 || nsize > 64 )
  324.            {    puts("-N must be between 2 and 64");
  325.             gs_exit(1);
  326.            }
  327. #endif
  328.         minst->name_table_size = (ulong)nsize << 10;
  329.        }
  330.         break;
  331.     case 'P':            /* choose whether search '.' first */
  332.         if ( !strcmp(arg, "") )
  333.           minst->search_here_first = true;
  334.         else if ( !strcmp(arg, "-") )
  335.           minst->search_here_first = false;
  336.         else
  337.           {    puts("Only -P or -P- is allowed.");
  338.             gs_exit(1);
  339.           }
  340.         break;
  341.     case 'q':            /* quiet startup */
  342.         gs_main_init1(minst);
  343.         initial_enter_name("QUIET", &vtrue);
  344.         break;
  345.     case 'r':            /* define device resolution */
  346.        {    float xres, yres;
  347.         ref value;
  348.         gs_main_init1(minst);
  349.         switch ( sscanf((const char *)arg, "%fx%f", &xres, &yres) )
  350.            {
  351.         default:
  352.             puts("-r must be followed by <res> or <xres>x<yres>");
  353.             gs_exit(1);
  354.         case 1:            /* -r<res> */
  355.             yres = xres;
  356.         case 2:            /* -r<xres>x<yres> */
  357.             make_real(&value, xres);
  358.             initial_enter_name("DEVICEXRESOLUTION", &value);
  359.             make_real(&value, yres);
  360.             initial_enter_name("DEVICEYRESOLUTION", &value);
  361.             initial_enter_name("FIXEDRESOLUTION", &vtrue);
  362.            }
  363.         break;
  364.        }
  365.     case 'D':            /* define name */
  366.     case 'd':
  367.     case 'S':            /* define name as string */
  368.     case 's':
  369.        {    char *adef = arg_heap_copy(arg);
  370.         char *eqp = strchr(adef, '=');
  371.         bool isd = (sw == 'D' || sw == 'd');
  372.         ref value;
  373.  
  374.         if ( eqp == NULL )
  375.             eqp = strchr(adef, '#');
  376.         /* Initialize the object memory, scanner, and */
  377.         /* name table now if needed. */
  378.         gs_main_init1(minst);
  379.         if ( eqp == adef )
  380.            {    puts("Usage: -dname, -dname=token, -sname=string");
  381.             gs_exit(1);
  382.            }
  383.         if ( eqp == NULL )
  384.            {    if ( isd )
  385.                 make_true(&value);
  386.             else
  387.                 make_empty_string(&value, a_readonly);
  388.            }
  389.         else
  390.            {    int code;
  391.             uint space = icurrent_space;
  392.  
  393.             *eqp++ = 0;
  394.             ialloc_set_space(idmemory, avm_system);
  395.             if ( isd )
  396.                {    stream astream;
  397.                 scanner_state state;
  398.  
  399.                 sread_string(&astream,
  400.                          (const byte *)eqp, strlen(eqp));
  401.                 scanner_state_init(&state, false);
  402.                 code = scan_token(&astream, &value, &state);
  403.                 if ( code )
  404.                    {    puts("-dname= must be followed by a valid token");
  405.                     gs_exit(1);
  406.                    }
  407.                 if ( r_has_type_attrs(&value, t_name,
  408.                               a_executable) )
  409.                   { ref nsref;
  410.                     name_string_ref(&value, &nsref);
  411. #define string_is(nsref, str, len)\
  412.   (r_size(&(nsref)) == (len) &&\
  413.    !strncmp((const char *)(nsref).value.const_bytes, str, (len)))
  414.                     if ( string_is(nsref, "null", 4) )
  415.                       make_null(&value);
  416.                     else if ( string_is(nsref, "true", 4) )
  417.                       make_true(&value);
  418.                     else if ( string_is(nsref, "false", 5) )
  419.                       make_false(&value);
  420.                     else
  421.                       { puts("-dvar=name requires name=null, true, or false");
  422.                         gs_exit(1);
  423.                       }
  424. #undef name_is_string
  425.                   }
  426.                }
  427.             else
  428.                {    int len = strlen(eqp);
  429.                 char *str = gs_malloc((uint)len, 1, "-s");
  430.  
  431.                 if ( str == 0 )
  432.                    {    lprintf("Out of memory!\n");
  433.                     gs_exit(1);
  434.                    }
  435.                 memcpy(str, eqp, len);
  436.                 make_const_string(&value,
  437.                     a_readonly | avm_foreign,
  438.                     len, (const byte *)str);
  439.                }
  440.             ialloc_set_space(idmemory, space);
  441.            }
  442.         /* Enter the name in systemdict. */
  443.         initial_enter_name(adef, &value);
  444.         break;
  445.        }
  446.     case 'u':            /* undefine name */
  447.         if ( !*arg )
  448.           { puts("-u requires a name to undefine.");
  449.             gs_exit(1);
  450.           }
  451.         gs_main_init1(minst);
  452.         initial_remove_name(arg);
  453.         break;
  454.     case 'v':            /* print revision */
  455.         print_revision();
  456.         gs_exit(0);
  457. /*#ifdef DEBUG*/
  458.     /*
  459.      * Here we provide a place for inserting debugging code that can be
  460.      * run in place of the normal interpreter code.
  461.      */
  462.     case 'X':
  463.         gs_main_init2(minst);
  464.       {    int xec;        /* exit_code */
  465.         ref xeo;        /* error_object */
  466. #define start_x()\
  467.   gs_main_run_string_begin(minst, 1, &xec, &xeo)
  468. #define run_x(str)\
  469.   gs_main_run_string_continue(minst, str, strlen(str), 1, &xec, &xeo)
  470. #define stop_x()\
  471.   gs_main_run_string_end(minst, 1, &xec, &xeo)
  472.         start_x();
  473.         run_x("\216\003abc");
  474.         run_x("== flush\n");
  475.         stop_x();
  476.       }
  477.         gs_exit(0);
  478. /*#endif*/
  479.     case 'Z':
  480.         { byte value = (*arg == '-' ? (++arg, 0) : 0xff);
  481.           while ( *arg )
  482.             gs_debug[*arg++ & 127] = value;
  483.         }
  484.         break;
  485.        }
  486.     return 0;
  487. }
  488.  
  489. /* Define versions of strlen and strcat that encode strings in hex. */
  490. /* This is so we can enter escaped characters regardless of whether */
  491. /* the Level 1 convention of ignoring \s in strings-within-strings */
  492. /* is being observed (sigh). */
  493. private int
  494. esc_strlen(const char *str)
  495. {    return strlen(str) * 2 + 2;
  496. }
  497. private void
  498. esc_strcat(char *dest, const char *src)
  499. {    char *d = dest + strlen(dest);
  500.     const char *p;
  501.     static const char *hex = "0123456789abcdef";
  502.     *d++ = '<';
  503.     for ( p = src; *p; p++ )
  504.     {    byte c = (byte)*p;
  505.         *d++ = hex[c >> 4];
  506.         *d++ = hex[c & 0xf];
  507.     }
  508.     *d++ = '>';
  509.     *d = 0;
  510. }
  511.  
  512. /* Process file names */
  513. private void
  514. argproc(gs_main_instance *minst, const char *arg)
  515. {    runarg(minst, "", arg, ".runfile", true, true);
  516. }
  517. private void
  518. runarg(gs_main_instance *minst, const char *pre, const char *arg,
  519.   const char *post, bool init, bool flush)
  520. {    int len = strlen(pre) + esc_strlen(arg) + strlen(post) + 1;
  521.     char *line;
  522.     if ( init )
  523.       gs_main_init2(minst);    /* Finish initialization */
  524.     line = gs_malloc(len, 1, "argproc");
  525.     if ( line == 0 )
  526.     {    lprintf("Out of memory!\n");
  527.         gs_exit(1);
  528.     }
  529.     strcpy(line, pre);
  530.     esc_strcat(line, arg);
  531.     strcat(line, post);
  532.     run_string(minst, line, flush);
  533. }
  534. private void
  535. run_string(gs_main_instance *minst, const char *str, bool flush)
  536. {    int exit_code;
  537.     ref error_object;
  538.     int code = gs_main_run_string(minst, str, minst->user_errors,
  539.                       &exit_code, &error_object);
  540.     if ( flush || code != 0 )
  541.     {    zflush(osp);        /* flush stdout */
  542.         zflushpage(osp);    /* force display update */
  543.     }
  544.     run_finish(code, exit_code, &error_object);
  545. }
  546. private void
  547. run_finish(int code, int exit_code, ref *perror_object)
  548. {    switch ( code )
  549.     {
  550.     case 0:
  551.         break;
  552.     case e_Quit:
  553.         gs_exit(0);
  554.     case e_Fatal:
  555.         eprintf1("Unrecoverable error, exit code %d\n", exit_code);
  556.         gs_exit(exit_code);
  557.     default:
  558.         gs_debug_dump_stack(code, perror_object);
  559.         gs_exit_with_code(255, code);
  560.     }
  561. }
  562.  
  563. /* ---------------- Print information ---------------- */
  564.  
  565. /*
  566.  * Help strings.  We have to break them up into parts, because
  567.  * the Watcom compiler has a limit of 510 characters for a single token.
  568.  * For PC displays, we want to limit the strings to 24 lines.
  569.  */
  570. private const char far_data help_usage1[] = "\
  571. Usage: gs [switches] [file1.ps file2.ps ...]\n\
  572. Most frequently used switches: (you can use # in place of =)\n\
  573.  -dNOPAUSE           no pause after page   | -q       `quiet', fewer messages\n\
  574.  -g<width>x<height>  page size in pixels   | -r<res>  pixels/inch resolution\n";
  575. private const char far_data help_usage2[] = "\
  576.  -sDEVICE=<devname>  select device         | -dBATCH  exit after last file\n\
  577.  -sOutputFile=<file> select output file: - for stdout, |command for pipe,\n\
  578.                                          embed %d or %ld for page #\n";
  579. private const char far_data help_trailer[] = "\
  580. For more information, see %s%suse.txt.\n\
  581. Report bugs to %s; use the form in bug-form.txt.\n";
  582. private const char far_data help_devices[] = "Available devices:";
  583. private const char far_data help_emulators[] = "Input formats:";
  584. private const char far_data help_paths[] = "Search path:";
  585.  
  586. /* Print the standard help message. */
  587. private void
  588. print_help(gs_main_instance *minst)
  589. {    print_revision();
  590.     print_usage();
  591.     print_emulators();
  592.     print_devices();
  593.     print_paths(minst);
  594.     print_help_trailer();
  595. }
  596.  
  597. /* Print the revision, revision date, and copyright. */
  598. private void
  599. print_revision(void)
  600. {    fprintf(stdout, "%s ", gs_product);
  601.     print_version();
  602.     fprintf(stdout, " (%d-%d-%d)\n%s\n",
  603.         (int)(gs_revisiondate / 10000),
  604.         (int)(gs_revisiondate / 100 % 100),
  605.         (int)(gs_revisiondate % 100),
  606.         gs_copyright);
  607. }
  608.  
  609. /* Print the version number. */
  610. private void
  611. print_version(void)
  612. {    fprintf(stdout, "%d.%02d",
  613.         (int)(gs_revision / 100),
  614.         (int)(gs_revision % 100));
  615. }
  616.  
  617. /* Print usage information. */
  618. private void
  619. print_usage(void)
  620. {    fprintf(stdout, "%s", help_usage1);
  621.     fprintf(stdout, "%s", help_usage2);
  622. }
  623.  
  624. /* Print the list of available devices. */
  625. private void
  626. print_devices(void)
  627. {    fprintf(stdout, "%s", help_devices);
  628.     {    int i;
  629.         int pos = 100;
  630.         const gx_device *pdev;
  631.  
  632.         for ( i = 0; (pdev = gs_getdevice(i)) != 0; i++ )
  633.           { const char *dname = gs_devicename(pdev);
  634.             int len = strlen(dname);
  635.  
  636.             if ( pos + 1 + len > 76 )
  637.               fprintf(stdout, "\n  "), pos = 2;
  638.             fprintf(stdout, " %s", dname);
  639.             pos += 1 + len;
  640.           }
  641.     }
  642.     fprintf(stdout, "\n");
  643. }
  644.  
  645. /* Print the list of language emulators. */
  646. private void
  647. print_emulators(void)
  648. {    fprintf(stdout, "%s", help_emulators);
  649.     {    const ref *pes;
  650.         for ( pes = gs_emulator_name_array;
  651.               pes->value.const_bytes != 0; pes++
  652.             )
  653.           fprintf(stdout, " %s", pes->value.const_bytes);
  654.     }
  655.     fprintf(stdout, "\n");
  656. }
  657.  
  658. /* Print the search paths. */
  659. private void
  660. print_paths(gs_main_instance *minst)
  661. {    fprintf(stdout, "%s", help_paths);
  662.     gs_main_set_lib_paths(minst);
  663.     {    uint count = r_size(&minst->lib_path.list);
  664.         uint i;
  665.         int pos = 100;
  666.         char fsepr[3];
  667.  
  668.         fsepr[0] = ' ', fsepr[1] = gp_file_name_list_separator,
  669.           fsepr[2] = 0;
  670.         for ( i = 0; i < count; ++i )
  671.         {    const ref *prdir =
  672.               minst->lib_path.list.value.refs + i;
  673.             uint len = r_size(prdir);
  674.             const char *sepr = (i == count - 1 ? "" : fsepr);
  675.  
  676.             if ( 1 + pos + strlen(sepr) + len > 76 )
  677.               fprintf(stdout, "\n  "), pos = 2;
  678.             fprintf(stdout, " ");
  679.             /*
  680.              * This is really ugly, but it's necessary.
  681.              * We wish we could just do:
  682.                 fwrite(prdir->value.bytes, 1, len, stdout);
  683.              */
  684.             { const char *p = (const char *)prdir->value.bytes;
  685.               uint j;
  686.               for ( j = len; j; j-- )
  687.                 fprintf(stdout, "%c", *p++);
  688.             }
  689.             fprintf(stdout, sepr);
  690.             pos += 1 + len + strlen(sepr);
  691.         }
  692.     }
  693.     fprintf(stdout, "\n");
  694. }
  695.  
  696. /* Print the help trailer. */
  697. private void
  698. print_help_trailer(void)
  699. {    fprintf(stdout, help_trailer, gs_doc_directory,
  700.         gp_file_name_concat_string(gs_doc_directory,
  701.                        strlen(gs_doc_directory),
  702.                        "use.txt", 7),
  703.         GS_BUG_MAILBOX);
  704. }
  705.